#include "../core/define.h"
#include "../define.h"
#include "material.h"

namespace sleek
{
    namespace mesh
    {
        material::material() : psize(1.f)
        {
            scale = core::math::vector3df(1);
            fac = rfc_off;
            ant = ral_off;
            mode = rmd_polygon;
            mat = rmt_solid;
            shd = rsd_smooth;
            for(core::u16 i = 0; i<GL_MAX_TEXTURE; ++i)
                Texture.push_back(0);
            effect = 0;
        }
        material::~material()
        {
        }

        void material::setMode(render_mode i)
        {
            mode = i;
        }
        void material::setShader(shader *i)
        {
            effect = i;
        }
        void material::setPointSize(core::f32 i)
        {
            psize = i;
        }
        void material::setShadeModel(core::u32 i)
        {
            shd = i;
        }
        void material::setFaceCulling(core::u32 i)
        {
            fac = i;
        }
        void material::setMaterialRender(core::u32 i)
        {
            mat = i;
        }
        void material::setAntialiasing(core::u32 i)
        {
            ant = i;
        }
        void material::setScale(core::math::vector3df i)
        {
            scale = i;
        }
        void material::setTexture(core::u32 i, texture::texture *t)
        {
            if(i<= GL_MAX_TEXTURE)
                Texture[i] = t;
        }
        /** ***************************************************** **/
        texture::texture* material::getTexture(core::u32 i)
        {
            return Texture[i];
        }
        core::math::vector3df material::getScale()
        {
            return scale;
        }
        core::u32 material::getMaterialRender()
        {
            return mat;
        }
        core::u32 material::getAntialiasing()
        {
            return ant;
        }
        core::u32 material::getFaceCulling()
        {
            return fac;
        }
        core::u32 material::getShadeModel()
        {
            return shd;
        }
        core::f32 material::getPointSize()
        {
            return psize;
        }
        shader* material::getShader()
        {
            return effect;
        }
        GLuint material::getMode()
        {
            switch(mode)
            {
                case rmd_point:     return GL_POINTS;    break;
                case rmd_line:      return GL_LINES;     break;
                case rmd_line_loop: return GL_LINE_LOOP; break;
                case rmd_polygon:   return GL_TRIANGLES; break;
                case rmd_quad:      return GL_QUADS;     break;
                default: return 0;
            }
        }
        void material::activeTexture(core::u32 stage, texture::texture *s)
        {
            if(stage > GL_MAX_TEXTURE) return;
            if(!s){ return; }

            glActiveTextureARB(GL_TEXTURE0_ARB+stage);

            glEnable(GL_TEXTURE_2D);
            if(stage == 0)
            {
                glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
            }
            else
            {
                glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
            }
            glBindTexture(GL_TEXTURE_2D, s->glTexture());
        }
        void material::desactiveTexture(core::u32 stage, texture::texture *s)
        {
            if(stage > GL_MAX_TEXTURE) return;
            glActiveTexture(GL_TEXTURE0_ARB+stage);
            glDisable(GL_TEXTURE_2D);
        }
        void material::begin()
        {
            glEnable(GL_BLEND);

            glPointSize(psize);
            glLineWidth(psize);

            shd & rsd_flat ? glShadeModel(GL_FLAT) : glShadeModel(GL_SMOOTH);
            ant & ral_polygone ? glEnable(GL_POLYGON_SMOOTH) : glDisable(GL_POLYGON_SMOOTH);
            ant & ral_line ? glEnable(GL_LINE_SMOOTH) : glDisable(GL_LINE_SMOOTH);

            fac & rfc_back && !(fac & rfc_front) ? glCullFace(GL_BACK) : void();
            fac & rfc_front && !(fac & rfc_back) ? glCullFace(GL_FRONT) : void();
            fac & rfc_front && fac & rfc_back ? glCullFace(GL_FRONT_AND_BACK) : void();
            fac & rfc_off ? glDisable(GL_CULL_FACE) : glEnable(GL_CULL_FACE);

            mat & rmt_solid ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
            mat & rmt_lighting ? glEnable(GL_LIGHTING) : glDisable(GL_LIGHTING);
            //mat & rmt_fog ? glEnable(GL_FOG) : glDisable(GL_FOG);

            mat & rmt_add ? glBlendFunc(GL_ONE, GL_ONE) : void();
            mat & rmt_solid ? glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) : void();
            mat & rmt_sub ? glBlendFunc(GL_ZERO, GL_SRC_ALPHA) : void();

            if(effect)
            {
                effect->begin();
                effect->UpdateCallback();
            }
            else
            {
                activeTexture(0, Texture[0]); activeTexture(1, Texture[1]);
                activeTexture(2, Texture[2]); activeTexture(3, Texture[3]);
            }
        }
        void material::end()
        {
            if(effect) effect->end();
            else
            {
                desactiveTexture(3, 0); desactiveTexture(2, 0);
                desactiveTexture(1, 0); desactiveTexture(0, 0);
                glDisable(GL_BLEND);
            }
        }
    }
}
